<?php
//================================================================================>
/**
 * 
 * Extended class d, with add colored Trace, and possabilty to  save "images" of arrays and database ...
 * tables in to the memory, for comparing it later
 * @todo Make fixed width for rows in trace
 * @todo add colors to php files that display calls
 * @todo add order and limit for php file display calls
 * @author karpov
 *
 */
class df extends d{
    
    private static $is_this_first_call=true;
    
    
    //==================================================>
    /**
     * Data about DB
     * this values will be setet at the onnect_to_the_db method
     * and they will depends on the system settings    
     */
    private static $server="";
    private static $username="";
    private static $password="";
    private static $database="";
    
    private static $table_calls="log_deb_calls"; // table contain data about all debug information in this call
    private static $table_items="log_deb_items"; // table wich contain information about one debug call (array, object, sql)
    
    
    
    private static $db_res=false; // Current resourse ret by my sql connection
    
    private static $current_call_id=false; // The id of current call connection
    private static $current_caller_name=""; // The name of the class wich call this debug write to log
    //==================================================>
    
      /**
     *  Diman color
     *  I want to add colors to trace, so all calls of the same object methods will have same color
     */
    ///=================>
    //public static $log_colors=array("FFE187","FFF887", "F8FF87", "DFFF87", "BDFF87", "87FF8A",  "87FFBA", "87FF5D", "FDFF57", "C3FB6A", "FDC49E", 
    //"ACD663");
    public static $log_colors=array("D4FF8A","62D04C","A1FD00","7CC202","68FF6F","FFFAAF","FCF361","FCED03","C3DAFF","74A8FE","9A9AFF");
    public static $log_color_count=0; // Index of current color I'll use
    public static $log_arr_matched_callers=array(); // assocc class name - color; this array contain name on caller as kay, and color I've add to it
    public static $log_files_for_trace=array("C:/AmiroDebug/trace_color0.txt","C:/AmiroDebug/trace_color1.txt","C:/AmiroDebug/trace_color2.txt"); // Files for store color association
    public static $log_path_to_js="/dimatest/debugtools/js/trace.js"; // file where instrument for debuging stored
    ///=================>
    
    ///=================> For array output
    public static $log_arr_colors=array("FFE187","FFF887", "F8FF87", "DFFF87", "BDFF87", "87FF8A",  "87FFBA", "87FF5D", "FDFF57", "C3FB6A", "FDC49E", 
"ACD663");
    public static  $log_arr_color_count=0;
    public static $log_do_log=true; // Will we run log_ function
    public static $log_use_colors=true; // Can turn of the colors if tired of them
    public static $log_grey="E7E7E7";
    ///=================>
      
    /**
     * ECHO string with jquery and scripts use JS possabilities
     * Enter description here ...
     */
    public static function initJsScripts(){
        d::w("<script src=\"http://code.jquery.com/jquery-1.5.js\"></script>");
        d::w("<script src=\"".self::$log_path_to_js."\"></script>");
        
    }
      
      /**
     * Returns trace HTML as string.
     * 
     * Also this function color lines by Class name (there some ways how you can add colors look at the exaple)
     * and save this assocc {class name - color} at he serialized array in to the file stored here self::$log_files_for_trace
     *
     * Example:
     * <code>
     * echo d::getTraceAsString();
     * echo d::getTraceAsString(null,0); // Caller of one Class will have one color at row (DEFAULT)
     * echo d::getTraceAsString(null,1); // Rows will have same color if color Class and method are the same
     * echo d::getTraceAsString(null,2); // Rows will have same color if Class,Method, Number of args are the same
     * </code>
     *
     * @param  array $aTrace  Array having the same structure as debug_backtrace() result
     * @param int $m_type  The way the collor add to rows
     *        // 0 - Set color by Class or Function name (if function have no class name)
     *        // 1 - Set color for Class and Method or function name
     *        // 3 - Set color for Class Medho and Number of Args
     * @return string
     */
    public static function getTraceAsString(array $aTrace = null,  $m_type=0){
        
        //-------------------------------------------------------------------------------------------------------
        // Working with color assoc that will be saved in to the file
        //-------------------------------------------------------------------------------------------------------
        // ! CUZ IN LACK OF TIME, and written for my self do not create protection!!!!
        $m_type=(0==$m_type||1==$m_type)?$m_type:2; // have three modes and for each mod it own file
        
        $the_arr_file_name_for_log=self::$log_files_for_trace[$m_type];
        
        // if file exists read  data from this file
        if(file_exists($the_arr_file_name_for_log)){
            self::$log_arr_matched_callers=unserialize(file_get_contents($the_arr_file_name_for_log));
        }
        ////-------------------------------------------------------------------------------------------------------
        
        
        if(is_null($aTrace)){
            $aTrace = debug_backtrace();
            array_shift($aTrace);
        }
        $html =
            "<fieldset><legend> <b>Debug backtrace" .
            (isset($aTrace[0]['file']) ? ': ' . basename($aTrace[0]['file']) . ":" . $aTrace[0]['line'] : '') .
            "</b> </legend><table border='0' class='backtrace' width=100%>\n".
            "<tr style='background:#4BD239;font-weight:bold;color:white'>\n<th>File</th><th>Line</th><th>Caller</th><th>Args</th>\n<th><a class=\"trace_table_show\">[show]</a></th>\n</tr>\n";
        $count = 0;
        foreach($aTrace as $r){
            if(isset($r['args'])){
                $aParams = array();
                foreach($r['args'] as $arg){
                    if (is_object($arg)){
                        $aParams[] = 'Object';
                    }elseif(is_array($arg)){
                        $aParams[] = 'Array';
                    }else{
                        $aParams[] = (string)$arg;
                    }
                }
                $args = implode('<br />', $aParams);
            }else{
                $args = '';
            }
            $argsQty = isset($r['args']) ? count($r['args']) : '';
            $argsQty = $argsQty > 0 ? $argsQty : '';
            
            // <====================================================================================================
            // Selecting Colors
            $color_on_mouseover="D6FE8F";
            $color_standart="D6FE8F";
            
            // Is the ket of the array, we look do we have such key, if we have use color located there
            // if not select our new color
            // Different type the match working
            // 0 - Set color by Class or Function name (if function have no class name)
            // 1 - Set color for Class and Method or function name
            // 2 - Set color for Class Medho and Number of Args
            $match="NoneNoneInitValue162901042001"; // set strange value, cuz I need it to be uniq
            
            // Match Will be Class or Function name
            if(0==$m_type){
                if(isset($r['class'])) { $match=$r['class'];}
                elseif(isset($r['function']))  { $match=$r['function'];}
                else{$match="NoneNoneInitValue162901042001";}
            }
            // Color for each Class and method
          elseif(1==$m_type){
                if(isset($r['class'])) { $match=$r['class']."1637gule1638".$r['function'];}
                elseif(isset($r['function']))  { $match=$r['function'];}
                else{$match="NoneNoneInitValue162901042001";}
            }
            // Class method and number of args
            else{
                $m_type=2; // Need for storing and getting data from file assoc
                $match=(isset($r['class'])
                    ? $r['class'] . $r['type'] . $r['function'] . '(' . $argsQty . ')'
                    : (isset($r['function']) ? $r['function'] . '(' . $argsQty . ')' : 'NoneNoneInitValue162901042001') );
            }
            
            

                    
             $match=str_replace(">", "", $match);      
            
                    // No such match in array
                    if(!array_key_exists($match, self::$log_arr_matched_callers))
                    {
                        $c_i=(++self::$log_color_count)%count(self::$log_colors);
                        $color_standart=self::$log_colors[$c_i];
                        self::$log_arr_matched_callers[$match]=$color_standart;
                    }
                    // Have such match get color from array
                    else{
                        $color_standart=self::$log_arr_matched_callers[$match];
                    }
            // <=====================================================================================================
        
            $html .=
                "<tr style=\"background-color: #$color_standart;\"><td style=\"width: 500px;\">" . 
                (isset($r['file']) ? $r['file'] : '&nbsp;') . "</td><td style=\"width: 50px;\">" .
                (isset($r['line']) ? $r['line'] : '&nbsp;') . "</td><td style=\"width: 350px;\">" .
                (isset($r['class'])
                    ? $r['class'] . $r['type'] . $r['function'] . '(' . $argsQty . ')'
                    : (isset($r['function']) ? $r['function'] . '(' . $argsQty . ')' : '&nbsp;') ) . '</td>'.
                "<td style=\"width: 250px;\">" . $args . "</td><td style=\"width: 100px;\"><a class=\"debug_trace_control trace_tr_hide\">[hide]</a></td></tr>\n";
                    /*
                      '<tr onmouseover="this.style.background=\'#'.$color_on_mouseover.'\'" onmouseout="this.style.background=\'\'" ' .
               //($count++ % 2 ? 'bgcolor="#efefef"':'').
               "#$color_standart".
               '><td>' . (isset($r['file']) ? $r['file'] : '&nbsp;') . '</td><td>' .
                (isset($r['line']) ? $r['line'] : '&nbsp;') . '</td><td>' .
                (isset($r['class'])
                    ? $r['class'] . $r['type'] . $r['function'] . '(' . $argsQty . ')'
                    : (isset($r['function']) ? $r['function'] . '(' . $argsQty . ')' : '&nbsp;') ) . '</td>'.
                '<td>' . $args . "</td></tr>\n";*/
        }
        $html .= "</table></fieldset>\n";
        //---------------------------------------------------------------------------
        // Try to put array with collor assoc in to the file
        @file_put_contents($the_arr_file_name_for_log, serialize(self::$log_arr_matched_callers));
        //---------------------------------------------------------------------------
        return $html;
    }
    ///======================================================================================================>
    
    /**
     * 
     * Return colored html with padding for every nested element of the array $arr
     * it is recurcive functions
     * @param array $arr
     */
   public static function log_arr_rec($arr){
        $ans_str="";
        
        if(!is_array($arr)) {return "<hr><pre>".print_r($arr,true)."</pre><hr>"; }
        if(empty($arr)){
            return "<span style=\"background-color: #12FF00; font-size: 1.2em; font-weight: bolder; font-color: #A48371; padding: 1px; margin 1px; border: 1px solid black;\">Emtpy Array</span>";;
        }
        
        foreach($arr as $k=>$v){
        // Get color
        $c_i=(++self::$log_arr_color_count)%count(self::$log_arr_colors);
        $color=self::$log_arr_colors[$c_i];
        
            $ans_str.="<div style=\"background-color: #$color; margin: 0 0 0 30px; padding 0px; border: 1px dashed black;\">";
            $ans_str.="<b>$k </b>";
                if(!is_array($v)){
                    if(!empty($v))
                    {
                        // If it is not an object simply print it, but if it is object need to make print_r
                        if(!is_object($v)) {
                            $ans_str.="<span style=\"background-color: #E7F7CB\">$v</span>";
                            
                        }
                        // here it is object and we need say this is object, cuz convert to string reurn this:PHP: RECOVERABLE ERROR, and print_r simply pending
                        else{
                            $ans_str.="<span style=\"background-color: #FF1E00; width: 40px; font-size: 0.7em; font-weight: bolder; font-color: #A48371; padding: 1px; margin 1px; border: 1px solid black;\">OBJECT: class_name=".get_class($v)."</span>";
                        }
                    }
                    // Empty value under key
                    else {
                            $ans_str.="<span style=\"background-color: #12FF00; font-size: 0.7em; font-weight: bolder; font-color: #A48371; padding: 1px; margin 1px; border: 1px solid black;\">empty()</span>";
                    }
                }
                else{
                    $ans_str.="New arr===></br>";
                    $ans_str.=self::log_arr_rec($v);
                
                }
            $ans_str.="</div>";
            
        }
        return $ans_str;
    }
    
    /*
   * Cut sql by commas and make colored html (div), created from log_arr_rec
   * return string with html
   */
 public static function log_sql_comma($sql){
          $ans_str="";
        
          if(!is_string($sql)){
            return "The SQL argument must be a string!<br>";
        }
        
        $arr=explode(',', $sql);
        
        
        foreach($arr as $k=>$v){
        // Get color
        // Get color
        $c_i=(++self::$log_arr_color_count)%count(self::$log_arr_colors);
        $color=self::$log_arr_colors[$c_i];
        
            $ans_str.="<div style=\"background-color: #$color; margin: 0 0 0 30px; padding 0px; border: 1px dashed black;\">";
            $ans_str.="<b>$k </b>";
                if(!is_array($v)){
                    if(!empty($v))$ans_str.="<span style=\"background-color: #E7F7CB\">$v</span>";
                    else $ans_str.="<span style=\"background-color: #12FF00; font-size: 0.7em; font-weight: bolder; font-color: #A48371; padding: 1px; margin 1px; border: 1px solid black;\">empty()</span>";
                }
                else{
                    continue;
                }
            $ans_str.="</div>";
            
        }
        return $ans_str;
    }
    /**
     * 
     * Get data from table and create htlm table from this
     * @param unknown_type $tablename
     * @param unknown_type $what
     * @param unknown_type $where
     */
 public static function log_sql_table($tablename,$what="*",$where='1'){
          
        
          if(!is_string($tablename)){
            return "The table name must be a string<br>";
        }
        
        $sql="SELECT $what FROM $tablename WHERE $where;";
        
        // Running sql
        $ans=mysql_query($sql,self::$db_res);
        if(false===$ans){
            return "<div style=\"background-color: red;\">".mysql_error(self::$db_res)."</div>";
        }
        
        $flag=0;
        $html="<h2>$sql</h2>";
        $html.="<table>";
        while($row=mysql_fetch_assoc($ans)){
            if(0==$flag){
             $flag=1;
             $c_i=(++self::$log_arr_color_count)%count(self::$log_arr_colors);
            $color=self::$log_arr_colors[$c_i];
             $html.="<tr style=\"background-color: #$color; margin: 0 0 0 30px; padding 0px; border: 2px solid black; font-weight: bolder;\">";
                  foreach ($row as $name=>$value){
                      $html.="<th>$name</th>";
                  }
                 $html.="</tr>";
            }
            $c_i=(++self::$log_arr_color_count)%count(self::$log_arr_colors);
            $color=self::$log_arr_colors[$c_i];
             $html.="<tr style=\"background-color: #$color; margin: 0 0 0 30px; padding 0px; border: 1px dashed black;\">";
                  foreach ($row as $name=>$value){
                      $html.="<td>$value</td>";
                  }
                 $html.="</tr>";
            
        }
        $html.="</table>";
        return $html;
        
        
        /*foreach($arr as $k=>$v){
        // Get color
        // Get color
        $c_i=(++self::$log_arr_color_count)%count(self::$log_arr_colors);
        $color=self::$log_arr_colors[$c_i];
        
            $ans_str.="<div style=\"background-color: #$color; margin: 0 0 0 30px; padding 0px; border: 1px dashed black;\">";
            $ans_str.="<b>$k </b>";
                if(!is_array($v)){
                    if(!empty($v))$ans_str.="<span style=\"background-color: #E7F7CB\">$v</span>";
                    else $ans_str.="<span style=\"background-color: #12FF00; font-size: 0.7em; font-weight: bolder; font-color: #A48371; padding: 1px; margin 1px; border: 1px solid black;\">empty()</span>";
                }
                else{
                    continue;
                }
            $ans_str.="</div>";
            
        }
        return $ans_str;*/
    }
    
    /**
     * Connect to the bd, and create new row in caller table
     */
    private static function connect_to_the_db($small_descr="empty"){
        // First of all we need to get passwords from current version of system
        // so require password seeking class
        $debug_name='debug_fast'; // Name of the folder wehre all debuging scripts stored
        $debug_folder=self::SeekFolderAtParents($debug_name);
       
       // If we at the root (calling www.test.com) SeekFolderAtParents return false
       // and we try to requir dir from current position
        if(!$debug_folder) {
            $debug_folder=$debug_name;
        }
        
        require($debug_folder."/"."PasswordSeeker.php");
        
        $ini_folder=self::SeekFolderAtParents('_local');
       // as like as in the debug folder, if we at the root, we shall try to include here
        if(!$ini_folder){
            $ini_folder='_local';
        }
        $ini_path=$ini_folder."/"."config.ini.php";
        
        // Getting Mysql connection data
        // Class to grab this dat from ini file
        $PasswordSeekerObject=new PasswordSeeker($ini_path);
        self::$server = $PasswordSeekerObject->server;
        self::$username = $PasswordSeekerObject->username;
        self::$password = $PasswordSeekerObject->password;
        self::$database = $PasswordSeekerObject->database; 
        
        
        
        // connect to the db
        self::$db_res=mysql_connect(self::$server,self::$username,self::$password);
        if(false===self::$db_res){ die("cannot connect to database for print debug information");}
        
        // select database
        $ans=mysql_select_db(self::$database,self::$db_res);
        if(false==$ans){die(mysql_error(self::$db_res));}
        
        //Inserting new row in caller table 
        $sql="INSERT INTO ".self::$table_calls." SET time_=NOW(), caller='".self::$current_caller_name."', small_descr='$small_descr', big_descr='';";
        $ans=mysql_query($sql,self::$db_res);
        if(false==$ans){die(mysql_error(self::$db_res));}
        
        // Setting PID of
        self::$current_call_id=mysql_insert_id();
        //d::w("<h1 style=\"background-color: red;\">".self::$current_call_id."</h1>");
    }
    /**
     * a - array or any other var, but first of all it was created for debug arrays
     * save array or any other variable to the db ...
     * @param unknown_type $var
     */
    public static function a($var,$small_descr="empty"){
        $trace=debug_backtrace();
        
        // Get caller name  that will be saved in database
        $caller_name=""; // Here will be {class_name}::{method} or symply {function}
        $caller_arr=$trace[1];
        if(isset($caller_arr['class'])) { $caller_name=$caller_arr['class']."::".$caller_arr['function'];}
        elseif(isset($caller_arr['function']))  { $caller_name=$caller_arr['function'];}
        else{$caller_name="!empty!";}
        self::$current_caller_name=$caller_name;
        
        // If this is first call of this function in current script we need to connect to the bd
        // and create new row in Caller table
        if(self::$is_this_first_call){
            self::$is_this_first_call=false;
            self::connect_to_the_db($small_descr);
        }
        
        // Html I'll going to save in the database
        $html=mysql_real_escape_string(self::log_arr_rec($var));
        
        // Inserting debug data into db
        $sql="INSERT INTO log_deb_items 
    SET
    
    pid=".self::$current_call_id.",
    time_=NOW(), 
    caller='".self::$current_caller_name."', 
    TYPE='arr', 
    DATA='".$html."', 
    small_descr='$small_descr', 
    big_descr='big_descr';";
        
        
        $ans=mysql_query($sql,self::$db_res);
        if(false==$ans){die(mysql_error(self::$db_res));}
        
        // returning id of the current row
        return mysql_insert_id();
    }
    /**
     * t - write trace to database, created from a method
     */
    public static function t($small_descr="empty"){
        $trace=debug_backtrace();
        
        // Get caller name  that will be saved in database
        $caller_name=""; // Here will be {class_name}::{method} or symply {function}
        $caller_arr=$trace[1];
        if(isset($caller_arr['class'])) { $caller_name=$caller_arr['class']."::".$caller_arr['function'];}
        elseif(isset($caller_arr['function']))  { $caller_name=$caller_arr['function'];}
        else{$caller_name="!empty!";}
        self::$current_caller_name=$caller_name;
        
        // If this is first call of this function in current script we need to connect to the bd
        // and create new row in Caller table
        if(self::$is_this_first_call){
            self::$is_this_first_call=false;
            self::connect_to_the_db($small_descr);
        }
        
        // Html I'll going to save in the database
        $html=mysql_real_escape_string(self::getTraceAsString($trace));
        
        // Inserting debug data into db
        $sql="INSERT INTO log_deb_items 
    SET
    
    pid=".self::$current_call_id.",
    time_=NOW(), 
    caller='".self::$current_caller_name."', 
    TYPE='Trace', 
    DATA='".$html."', 
    small_descr='$small_descr', 
    big_descr='big_descr';";
        
        
        $ans=mysql_query($sql,self::$db_res);
        if(false==$ans){die(mysql_error(self::$db_res));}
        
        // returning id of the current row
        return mysql_insert_id();
    }
/**
     * sq - this was created for showing SQL query stings
     * separate sql string quety in commas (,)
     * @param unknown_type $var
     */
    public static function sq($var,$small_descr="empty"){
        $trace=debug_backtrace();
        
        // Get caller name  that will be saved in database
        $caller_name=""; // Here will be {class_name}::{method} or symply {function}
        $caller_arr=$trace[1];
        if(isset($caller_arr['class'])) { $caller_name=$caller_arr['class']."::".$caller_arr['function'];}
        elseif(isset($caller_arr['function']))  { $caller_name=$caller_arr['function'];}
        else{$caller_name="!empty!";}
        self::$current_caller_name=$caller_name;
        
        // If this is first call of this function in current script we need to connect to the bd
        // and create new row in Caller table
        if(self::$is_this_first_call){
            self::$is_this_first_call=false;
            self::connect_to_the_db($small_descr);
        }
        
        // Html I'll going to save in the database
        $html=mysql_real_escape_string(self::log_sql_comma($var));
        
        // Inserting debug data into db
        $sql="INSERT INTO log_deb_items 
    SET
    
    pid=".self::$current_call_id.",
    time_=NOW(), 
    caller='".self::$current_caller_name."', 
    TYPE='SQLQueryString', 
    DATA='".$html."', 
    small_descr='$small_descr', 
    big_descr='big_descr';";
        
        
        $ans=mysql_query($sql,self::$db_res);
        if(false==$ans){die(mysql_error(self::$db_res));}
        
        // returning id of the current row
        return mysql_insert_id();
    }
/**
 * 
 * Saving SQL Table
 * @param str $tablename SELECT {$what} FrOM {$tablename} WHERE {$were}
 * @param str $what SELECT {$what} FrOM {$tablename} WHERE {$were}
 * @param str  $where SELECT {$what} FrOM {$tablename} WHERE {$were}
 * @param str  $small_descr
 */
    public static function st($tablename,$what="*",$where="1",$small_descr=""){
        $trace=debug_backtrace();
        
        // Get caller name  that will be saved in database
        $caller_name=""; // Here will be {class_name}::{method} or symply {function}
        $caller_arr=$trace[1];
        if(isset($caller_arr['class'])) { $caller_name=$caller_arr['class']."::".$caller_arr['function'];}
        elseif(isset($caller_arr['function']))  { $caller_name=$caller_arr['function'];}
        else{$caller_name="!empty!";}
        self::$current_caller_name=$caller_name;
        
        // If this is first call of this function in current script we need to connect to the bd
        // and create new row in Caller table
        if(self::$is_this_first_call){
            self::$is_this_first_call=false;
            self::connect_to_the_db($small_descr);
        }
        
        // Html I'll going to save in the database
        $html=mysql_real_escape_string(self::log_sql_table($tablename,$what,$where));
        
        // Inserting debug data into db
        $sql="INSERT INTO log_deb_items 
    SET
    
    pid=".self::$current_call_id.",
    time_=NOW(), 
    caller='".self::$current_caller_name."', 
    TYPE='SQLTable', 
    DATA='".$html."', 
    small_descr='$small_descr', 
    big_descr='big_descr';";
        
        
        $ans=mysql_query($sql,self::$db_res);
        if(false==$ans){die(mysql_error(self::$db_res));}
        
        // returning id of the current row
        return mysql_insert_id();
    }
/**
* Climbing up to the dir tree to the root from current dir
* 
* @param mixed $folder_name - the folder we seeking for, only folder name without any slash
* @return false if we could not find dir, or path to this dir without trailing slash
*/
public static function SeekFolderAtParents($folder_name){
    $cur_dir = getcwd();
    
    $cur_dir=trim($cur_dir); // current dir for testing
    $old_dir= ""; // to determinate where to stop script
    
    // Climbing to the dir root, template for cutting of tail
    // have to add hack in REXEX cuz [/////] dos not work
    $cut_off_pattern="/\\\\{0,1}\/{0,1}(\w*)$/i";
    
    // Here will be stored paretns of current dir
    $childs_array=array();
   // Breking with loop at the and of block
    for( ;  ; ){
        $cur_dir=preg_replace($cut_off_pattern,"",$cur_dir);
        if(is_dir($cur_dir)){
            $childs_array=scandir($cur_dir);
            if(in_array($folder_name,$childs_array)){
                return $cur_dir."/".$folder_name;
            }
        }
        
        // We start to repeating the same, need to break
        if($old_dir==$cur_dir || (false==is_dir($cur_dir))) break;
        $old_dir=$cur_dir;
    }
    return false;
}
} // <<== end of the class
